Skip to content

feat(api): add @readied/api backend for cloud sync#72

Open
tomymaritano wants to merge 3 commits intodevelopfrom
feature/backend-api
Open

feat(api): add @readied/api backend for cloud sync#72
tomymaritano wants to merge 3 commits intodevelopfrom
feature/backend-api

Conversation

@tomymaritano
Copy link
Owner

Summary

  • Add new @readied/api package with Hono backend for cloud sync
  • Magic link authentication flow with JWT tokens
  • Push/pull sync endpoints for encrypted notes
  • Stripe webhook integration for Pro subscriptions
  • Drizzle ORM schema for Neon Postgres

Tech Stack

  • Hono - Edge runtime compatible web framework
  • Drizzle - Type-safe ORM
  • Neon - Serverless Postgres
  • jose - JWT handling
  • Zod - Request validation

API Endpoints

Auth

  • POST /auth/magic-link - Send magic link email
  • POST /auth/verify - Verify token, get JWT
  • POST /auth/refresh - Refresh access token

Sync (Protected)

  • GET /sync?cursor=0 - Pull changes
  • POST /sync - Push changes
  • GET /sync/status - Get sync status

Subscription

  • POST /subscription/webhook - Stripe webhooks
  • GET /subscription/status - Get plan status

Test plan

  • Typecheck passes (pnpm --filter @readied/api typecheck)
  • Local dev server starts (pnpm --filter @readied/api dev)
  • Deploy to Cloudflare Workers staging
  • Test auth flow with magic link
  • Test sync push/pull with mock data

🤖 Generated with Claude Code

Cloud sync backend for Pro tier:
- Magic link authentication via email
- Push/pull sync operations for encrypted notes
- Stripe webhook integration for subscriptions
- Drizzle ORM with Neon Postgres schema

Tech stack:
- Hono for edge runtime compatibility
- jose for JWT handling
- Zod for request validation
- Cloudflare Workers deployment ready

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea9211fe48

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +36 to +40
const { payload } = await jose.jwtVerify(token, secret, {
algorithms: ['HS256'],
});

if (!payload.sub || !payload.email) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject refresh tokens in auth middleware

Because the middleware only checks for sub and email after jwtVerify, a refresh token (which is also HS256-signed and contains those fields) can be used as a Bearer token on protected routes. That makes the 7‑day refresh token equivalent to an access token, undermining the intended 15‑minute access token window. This occurs whenever a client (or attacker with a leaked refresh token) sends it in the Authorization header. Consider validating a token type/claim and explicitly rejecting type: 'refresh' here.

Useful? React with 👍 / 👎.

Comment on lines +37 to +41
const body = await c.req.text();

// Verify webhook signature (simplified - in production use Stripe SDK)
// For now, we'll trust the signature and parse the event
// TODO: Implement proper signature verification with crypto.subtle

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Verify Stripe webhook signatures before processing

The webhook handler only checks that the stripe-signature header exists, then parses and processes the body without validating the signature. This means any caller who can reach the endpoint can forge events to activate/cancel subscriptions or mark payments failed. This is a security/billing risk in production; you should verify the signature with the Stripe SDK (or crypto.subtle) using STRIPE_WEBHOOK_SECRET before trusting the payload.

Useful? React with 👍 / 👎.

Comment on lines +127 to +131
// Get current max version for this user
const [maxVersionResult] = await db
.select({ maxVersion: sql<number>`COALESCE(MAX(${syncLog.version}), 0)` })
.from(syncLog)
.where(eq(syncLog.userId, userId));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prevent duplicate sync versions under concurrency

The push handler computes MAX(sync_log.version) once and then increments locally for each change. If two push requests for the same user run concurrently, they can both read the same max version and insert overlapping versions, which breaks the monotonic ordering your cursor logic relies on. This happens when multiple devices push at the same time. Consider using a database-side sequence/transaction with locking, or a unique constraint plus retry logic, to guarantee per‑user version uniqueness.

Useful? React with 👍 / 👎.

tomymaritano and others added 2 commits January 7, 2026 07:51
- Replace @neondatabase/serverless with @libsql/client
- Update schema from pg-core to sqlite-core types
- Use text columns with ISO strings for dates (SQLite compatible)
- Update drizzle.config.ts for Turso dialect
- Update wrangler.toml with Turso env vars

Benefits of Turso:
- SQLite distributed (same engine as desktop app)
- Edge-native, perfect for Cloudflare Workers
- Free tier: 9GB storage, 500M rows/month
- Ultra-low latency with edge replicas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Created 6 tables for cloud sync:
- users: User accounts with email
- magic_links: Passwordless auth tokens
- devices: Registered sync devices
- sync_log: Encrypted note changes
- sync_cursors: Per-device sync position
- subscriptions: Pro tier tracking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant